#!/usr/bin/env python
"""
Generates a bus multiplexer with the specified number of ports
"""

from __future__ import print_function

import argparse
import math
from jinja2 import Template

def main():
    parser = argparse.ArgumentParser(description=__doc__.strip())
    parser.add_argument('-p', '--ports',  type=int, default=2, help="number of ports")
    parser.add_argument('-n', '--name',   type=str, help="module name")
    parser.add_argument('-o', '--output', type=str, help="output file name")

    args = parser.parse_args()

    try:
        generate(**args.__dict__)
    except IOError as ex:
        print(ex)
        exit(1)

def generate(ports=2, name=None, output=None):
    if name is None:
        name = "bus_mux_{0}".format(ports)

    if output is None:
        output = name + ".v"

    print("Opening file '{0}'...".format(output))

    output_file = open(output, 'w')

    print("Generating {0} port bus mux {1}...".format(ports, name))

    select_width = int(math.ceil(math.log(ports, 2)))

    t = Template(u"""

// Language: Verilog 2001


/*--------------------------------------------------------------------------------------------------------------------------*\
                 
//------------------------------------------------  bus 连接线 -------------------------------------------------------
{%- for p in ports %}
                 
// bus slave {{p}} wire 
wire [7:0] s{{p}}_dat  ; 
wire [7:0] s{{p}}_rdt  ; 
wire [7:0] s{{p}}_adr ;  
wire s{{p}}_wen ;
wire [7:0] s{{p}}_addr;    // Slave address prefix
wire [7:0] s{{p}}_addr_msk; // Slave address prefix mask                 
{%- endfor %}   

//------------------------------------------------  bus 地址分配 ------------------------------------------------------- 
{%- for p in ports %}
assign {s{{p}}_addr , s{{p}}_addr_msk } = {8'h{{p}}0 , 8'hf0} ;
{%- endfor %}  
                            
例化模板:
{{name}} u_{{name}}  (
    .clk( ) , 
    .rst_n( ) ,
    .m_dat_i( )  ,  
    .m_dat_o( ) ,   
    .m_adr_i( ) , 
    .m_wen_i( )    ,
                 
    {%- for p in ports %}
                 
    //bus slave {{p}} output          
    .s{{p}}_dat_o( s{{p}}_dat)  , 
    .s{{p}}_dat_i( s{{p}}_rdt)  , 
    .s{{p}}_adr_o( s{{p}}_adr) , 
    .s{{p}}_wen_o( s{{p}}_wen)  ,
                 
    //bus slave {{p}} address configuration
    .s{{p}}_addr    ( s{{p}}_addr), // Slave address prefix
    .s{{p}}_addr_msk( s{{p}}_addr_msk){% if not loop.last %},{% else %} {% endif %} // Slave address prefix mask
     
    {%- endfor %}
)  ;          
\*--------------------------------------------------------------------------------------------------------------------------*/

                 
 `timescale 1 ns / 1 ps
`ifndef  __{{name}}__
`define  __{{name}}__
                 
/*
 * bus {{n}} port multiplexer
 */
module {{name}} 
(
    input  wire                    clk,
    input  wire                    rst_n,

    /*
     *  master input
     */             
    input  wire [7:0]  m_dat_i  ,  
    output wire [7:0]  m_dat_o  ,   
    input  wire [7:0]  m_adr_i , 
    input  wire        m_wen_i    ,
    {%- for p in ports %}

    /*
     * slave {{p}} output
     */
    output  wire [7:0]  s{{p}}_dat_o  , 
    input   wire [7:0]  s{{p}}_dat_i  , 
    output  wire [7:0]  s{{p}}_adr_o , 
    output  wire        s{{p}}_wen_o  ,
                 
    /*
     * slave {{p}} address configuration
     */
    input  wire [7:0]   s{{p}}_addr,     // Slave address prefix
    input  wire [7:0]   s{{p}}_addr_msk{% if not loop.last %},{% else %} {% endif %} // Slave address prefix mask
    {%- endfor %}
);
{% for p in ports %}
wire s{{p}}_sel = ~|((m_adr_i ^ s{{p}}_addr) & s{{p}}_addr_msk);
{%- endfor %}

{% for p in ports %}
// wire s{{p}}_sel = s{{p}}_match{% if p > 0 %} & ~({% for q in range(p) %}s{{q}}_match{% if not loop.last %} | {% endif %}{% endfor %}){% endif %};
{%- endfor %}


// master
assign m_dat_o = {% for p in ports %}s{{p}}_sel ? s{{p}}_dat_i :
                  {% endfor %}8'h0;
                 
// assign m_dat_o = {% for p in ports %}s{{p}}_dat_i{% if not loop.last %}|{% else %} {% endif %}{% endfor %};   
{% for p in ports %}
// slave {{p}}
assign s{{p}}_adr_o = m_adr_i & ~s{{p}}_addr_msk ; 
assign s{{p}}_dat_o  = m_dat_i ; 
assign s{{p}}_wen_o  = m_wen_i & s{{p}}_sel ;
{% endfor %}

endmodule

`endif 
""")
    
    output_file.write(t.render(
        n=ports,
        w=select_width,
        name=name,
        ports=range(ports)
    ))
    
    print("Done")

if __name__ == "__main__":
    main()

